#!/bin/bash

########## DEFAULT VALUES ###############################################
# boolean variables are '1' for true or anything else for false.
wait=10 # between outputs, in s
bps=555 # output bytes per s - more is faster - 555 is a good value on my machine - set to 0 to disable; requires pv to be installed.
colored=1 # 0 for no colors or if command output provides colors, 1 for randomized color for each block
stars=0 # sprinkle canvas witgh stars after showing command output
density=3 # how many stars do you want on screen, max., in percent. Not precise.
erase=0 # instead of simplyclearing the screen, erase it from top to bottom
erasetime=2 # how many seconds do you wnat the erase process to last. Not precise.
colors=( 1 2 3 4 5 6 ) # to work with tput. exclude black and white; bold is randomized inside the script

# each line produces a block of output (later split by newline & eval'd)
commands='toilet -t -f smmono12 -W -- "$(date +%R)"
toilet -t -f future -- "$(date +"%A %d %B")"

toilet -t -f future -- "$(uptime -p)"'


########## USER CUSTOMISATION ################
config="$XDG_CONFIG_HOME/termss/${0##*/}.conf"
[ -r "$config" ] && . "$config"
##############################################

grep -qw toilet <<<"$commands" && ! which toilet >/dev/null && commands='echo "Please install toilet!"
echo "Or configure other commands in $config"'

#~ dependencies for marquee1 script
for i in shuf od tput; do
    which "$i" >/dev/null || exit 1
done
# pv is an optional dependency
which pv >/dev/null || bps=0

# sleep as a builtin
for file in /usr/lib/bash/sleep /usr/lib32/bash/sleep /usr/lib64/bash/sleep; do
    [ -r "$file" ] && enable -f "$file" sleep && break
done
# Portable enough?

# let's assume terminal size doesn't change during script execution
term_cols=$(tput cols)
term_lines=$(tput lines)

rand_func() {
	# more random than RANDOM? networkworld.com/article/3208389/linux/unix-how-random-is-random.html
	num="$(od -vAn -N2 -tu < /dev/urandom )"
	#num=$RANDOM
	# if $1 is passed, make sure random number is >= that value. if not, minimum is 0.
	min=${1-0}
	(( num < min )) && num=$((num + min))
	echo $num
}

#~ if [[ "$colored" == "1" ]]; then
	#~ colmax=${#colchoice[*]}
	#~ colchoice=$(( $(rand_func $colmax) % colmax))
	#~ lastcolchoice=$colchoice
#~ fi

tput civis # cursor invisible
#~ echo -en "\e]12;10\a" # make cursor green (we need this later)

oldifs="$IFS"
#declare -A length

while :; do
	longest_all=0
	outputlines=0
	count=0
	colchoiceprevious=()

	# command execution, but no output - to calculate size of complete output
	IFS=$'\n'
	while read -r -- command ; do
		IFS="$oldifs"
		# one array field for each command's output
		output[count]="$(eval $command)"
		longest[count]=0
		while read -r -- line ; do
			# find longest line
			(( ${#line} > longest_all )) && longest_all=${#line}
			(( ${#line} > ${longest[count]} )) && longest[count]=${#line}
			((outputlines++))
		done <<<"${output[count]}"
		IFS=$'\n'
		((count++))
	done <<<"$commands"
	IFS="$oldifs"

	# put cursor on random vertical position, i.e. line
	scope=$(( term_lines - outputlines ))
	vpos=0
	(( scope > 0 )) && vpos="$(( $(rand_func $scope) % scope ))" && tput vpa "$vpos"
	# random horizontal pos. offset, used later to put cursor
	scope=$(( term_cols - longest_all ))
	(( scope > 0 )) && offset_all=$(( $(rand_func $scope) % scope )) || offset_all=0

	# define 1 color for each command
	if [[ "$colored" == "1" ]]; then
	colchoice=( $(shuf -n$count -e ${colors[@]}) ) # once to initialise
		until [[ "${colchoice[@]}" != "${colchoiceprevious[@]}" ]]; do
		colchoice=( $(shuf -n$count -e ${colors[@]}) )
		done
		colchoiceprevious=( ${colchoice[@]} )
	fi

	# write text to random position
	for (( i=0 ; i<count ; i++ )) ; do
		[[ "$colored" == "1" ]] && tput setaf $(( ${colchoice[i]} + 8 * ( $(rand_func 2)%2 ) )) # randomly makes the color bold or not, by adding 8 to the random value, or not
		offset=$(( ( longest_all - ${longest[i]} ) / 2 + offset_all )) # this centers the blocks on top of each other
		IFS='' # necessary to include leading whitespace in line
		if (( bps > 0 )); then
			while read -r -- line ; do
				tput hpa $offset # place cursor horizontally
				tput cnorm
				printf "%s" "$line"
				tput civis
				printf "\n"
			done <<<"${output[i]}" | pv -L $bps -q --
		else
			while read -r -- line ; do
				tput hpa $offset # place cursor horizontally
				echo "$line"
			done <<<"${output[i]}"
		fi
		IFS="$oldifs"

		#~ if [[ "$colored" == "1" ]]; then
			#~ until [[ "$colchoice" != "$lastcolchoice" ]]; do colchoice=$(( $(rand_func $colmax) % colmax)); done
			#~ lastcolchoice=$colchoice
		#~ fi
	done

	if [[ "$stars" == 1 ]]; then
		# convert density to absolute number of stars desired
		density="$(bc <<<"scale=10;$term_cols*$term_lines/100*$density")"
		sleep "$(bc <<<"scale=10;$wait/10")"
		tput sgr0 # reset
		chars='*∙·•·•·•∙·•·•·•+◆●☼'
		#strings -e S -s '' /dev/urandom | while read -n1 char; do
		starsleep="$(bc <<<"scale=10;($wait-($wait/10))/$density")"
		while sleep "$starsleep"; do
			# put cursor on random position
			Y=$(( $(rand_func $term_lines) % term_lines ))
			X=$(( $(rand_func $term_cols) % term_cols ))
			if ((X<offset_all || X>=offset_all+longest_all)) || \
					((Y<vpos || Y>=vpos+outputlines)); then
				tput cup $Y $X
				#~ # put cursor on random vertical position, i.e. line
				#~ scope=$term_lines
				#~ tput vpa $(( $(rand_func $scope) % scope ))
				#~ # random horizontal pos. i.e. column
				#~ scope=$term_cols
				#~ tput hpa $(( $(rand_func $scope) % scope ))
				#tput setaf $(( ${colors[$(( $(rand_func ${#colors[@]})%${#colors[@]} ))]} ))
				printf '\e[%s;37m%s' "$((RANDOM%2))" "${chars:$((RANDOM%${#chars})):1}"
			fi
		done
	fi & pid=$!
	sleep $wait
	[[ "$stars" == 1 ]] && kill $pid >/dev/null 2>&1

	if [[ "$erase" == 1 ]] && ((erasetime>0)) && which pv >/dev/null; then
		tput -S <<!
cup 0 0
cnorm
!
		# cursor visible (normal)
		#~ screenchars="$((term_lines * term_cols))"
		#~ screenchars="$((screenchars/10))" # 10 spaces, therefore screenchars/10
		#~ ((erasetime > 0)) && delay="$(bc <<<"scale=10;$erasetime/$screenchars")" || delay=0.0002
		#~ for (( i=0;i<screenchars;i++ )); do
			#~ printf '          ' # 10 spaces, therefore screenchars/10
			#~ sleep "$delay"
		#~ done
		screenchars="$((term_lines * term_cols))"
		bps_cls="$((screenchars/erasetime))"
		for (( i=0;i<screenchars;i++ )); do
			printf " "
		done | pv -L $bps_cls -q
		tput -S <<!
civis
cup 0 0
!
	else
		clear
	fi
done 2>/dev/null
